#include "yamlfile.hpp"
#include <stdio.h>
#include <iostream>

using namespace Yaml;
using namespace std;

Parser::Parser(const string& filename)
{
  FILE*       _fd;
  size_t      size;

  _fd = fopen(filename.c_str(), "r");
  if (_fd == 0)
    throw YamlException(YamlException::ErrOpenFile);
  if (!(yaml_parser_initialize(&_parser)))
    throw YamlException(YamlException::ErrInitialize);
  fseek(_fd, 0, SEEK_END);
  size = ftell(_fd);
  fseek(_fd, 0, SEEK_SET);
  _buffer = new unsigned char[size + 1];
  fread(_buffer, size + 1, 1, _fd);
  fclose(_fd);
  yaml_parser_set_input_string(&_parser, _buffer, size);
  
  _lines.push_back(_buffer);
  for (unsigned int i = 0 ; i < size ; ++i)
  {
    if (_buffer[i] != '\n')
      continue ;
    _lines.push_back(&_buffer[i + 1]);
  }
}

Parser::~Parser()
{
  yaml_parser_delete(&_parser);
  delete[] _buffer;
}

DataTree*   Parser::Parsed(unsigned int i)
{
  if (i >= _parsed.size())
    return (0);
  return (_parsed[i]);
}

DataBranch* Parser::ScalarEvent(const std::string& word, DataBranch* current, DataBranch* waiting)
{
  if (waiting != 0)
  {
    waiting->value = word;
    return (0);
  }
  DataBranch* leave = new DataBranch;
  leave->key    = word;
  leave->father = current;
  leave->nil    = false;
  return (leave);
}

bool Parser::Run()
{
  DataTree*             file;
  DataBranch*           currentBranch = 0;
  DataBranch*           waitingValue  = 0;
  yaml_event_t          event;
  int                   lastLine      = 0;
  int                   lastCol       = 0;

  do
  {
    string         symbol;
    unsigned char  *line, *endLine;
    size_t         len;
    yaml_mark_t    beg, end;

    lastLine = 0;
    lastCol  = 0;
    yaml_parser_parse(&_parser, &event);
    beg     = event.start_mark;
    end     = event.end_mark;
    line    = _lines[beg.line];
    endLine = _lines[end.line];
    len = (((endLine - line) - beg.column) + end.column);
    if (len != 0)
    {
      const char* str = reinterpret_cast<char*>(line + beg.column);
      
      symbol.insert(0, str, len);
    }
    switch (event.type)
    {
      case YAML_STREAM_START_EVENT:
	break ;
      case YAML_STREAM_END_EVENT:
	break ;
      case YAML_DOCUMENT_START_EVENT:
	file          = new DataTree;
	currentBranch = file;
	break ;
      case YAML_DOCUMENT_END_EVENT:
	_parsed.push_back(file);
	file          = 0;
	currentBranch = 0;
	waitingValue  = 0;
	break ;
      case YAML_SEQUENCE_START_EVENT:
	break ;
      case YAML_SEQUENCE_END_EVENT:
	break ;
      case YAML_MAPPING_START_EVENT:
	if (waitingValue)
	{
	  currentBranch = waitingValue;
	  waitingValue  = 0;
	}
	break ;
      case YAML_MAPPING_END_EVENT:
	if (currentBranch)
	  currentBranch = currentBranch->father;
	break ;
      case YAML_SCALAR_EVENT:
	waitingValue = ScalarEvent(symbol, currentBranch, waitingValue);
	if (waitingValue)
	  currentBranch->children.push_back(waitingValue);
	break ;
      case YAML_ALIAS_EVENT:
	break ;
      case YAML_NO_EVENT:
        throw YamlException(YamlException::ErrSyntax, lastLine, lastCol);
    }
    if (event.type != YAML_STREAM_END_EVENT)
      yaml_event_delete(&event);
    lastLine = end.line;
    lastCol  = end.column;
  } while (event.type != YAML_STREAM_END_EVENT);
  yaml_event_delete(&event);
  return (true);
}
